package net.protocol.diameter.dictionary;

/**
 * <pre>
 * <p><b>Message Format:</b>
 * The Re-Auth-Request (RAR)
 *       &lt;RAR&gt;  ::= &lt; Diameter Header: 258, REQ, PXY &gt;
 *                  &lt; Session-Id &gt;
 *                  { Origin-Host }
 *                  { Origin-Realm }
 *                  { Destination-Realm }
 *                  { Destination-Host }
 *                  { Auth-Application-Id }
 *                  { Re-Auth-Request-Type }
 *                  [ User-Name ]
 *                  [ Origin-State-Id ]
 *                * [ Proxy-Info ]
 *                * [ Route-Record ]
 *                * [ AVP ]
 * 
 * The Re-Auth-Answer (RAA)
 *       &lt;RAA&gt;  ::= &lt; Diameter Header: 258, PXY &gt;
 *                  &lt; Session-Id &gt;
 *                  { Result-Code }
 *                  { Origin-Host }
 *                  { Origin-Realm }
 *                  [ User-Name ]
 *                  [ Origin-State-Id ]
 *                  [ Error-Message ]
 *                  [ Error-Reporting-Host ]
 *                * [ Failed-AVP ]
 *                * [ Redirect-Host ]
 *                  [ Redirect-Host-Usage ]
 *                  [ Redirect-Host-Cache-Time ]
 *                * [ Proxy-Info ]
 *                * [ AVP ]
 * 
 * <p><b>Sample usages:</b>
 *      return new MessageFormat(258, "RAR", true, true, 16777238);
 *      return new MessageFormat(258, "RAR", true, true, 16777238, "Re-Auth-Request");
 *      
 *      return new MessageFormat.Builder(258)
 *              .name("RAR")
 *              .request(true)
 *              .proxiable(true)
 *              .appId(16777238)
 *              .fullName("Re-Auth-Request")
 *              .build();
 *      ...
 *      messageFormat.add(Session-Id);
 *      messageFormat.add(Origin-Host);
 *      ...
 *      
 *      for (AVPRule e : DiameterFormat) {
 *          ...
 *      }
 * </pre>
 * 
 * @author jinhongw@gmail.com
 */
public class MessageFormat extends DiameterFormat {
	private final boolean request;
	private final boolean proxiable;
    
	private final int appId;
    
    private final String fullName;
    
    /**
     * @param code Command-Code(257)
     * @param name Command-Name-Abbrev(CER)
     * @param request message is an request or answer
     * @param proxiable message be proxied or not
     * @param appId App-Id
     */
	public MessageFormat(int code, String name, boolean request,
			boolean proxiable, int appId) {
		this(code, name, request, proxiable, appId, null);
	}

    /**
     * @param code Command-Code(257)
     * @param name Command-Name-Abbrev(CER)
     * @param request message is an request or answer
     * @param proxiable message be proxied or not
     * @param appId App-Id
     * @param fullName Command-Name(Capabilities-Exchange-Request)
     */
	public MessageFormat(int code, String name, boolean request,
			boolean proxiable, int appId, String fullName) {
		super(code, name);

		this.request = request;
		this.proxiable = proxiable;
		this.appId = appId;
		this.fullName = fullName;
	}

	/**
	 * If set, the message is a request. If cleared, the message is an answer.
	 * Default value is false
	 * 
	 * @return a <code>boolean</code> indicating whether or not message is an request.
	 */
	public boolean isRequest() {
		return request;
	}

	/**
	 * If set, the message MAY be proxied, relayed or redirected. If cleared,
	 * the message MUST be locally processed.
	 * 
	 * @return a <code>boolean</code> indicating whether or not message be proxied.
	 */
	public boolean isProxiable() {
		return proxiable;
	}
    
    public int getAppId() {
        return appId;
    }

    public String getFullName() {
		return fullName;
	}

	@Override
    public final boolean isMessage() {
        return true;
    }

    @Override
    public boolean isGroup() {
        return false;
    }
    
    @Override
	public int hashCode() {
		final int prime = 31;
		int result = super.hashCode();
		result = prime * result + appId;
		result = prime * result + (proxiable ? 1231 : 1237);
		result = prime * result + (request ? 1231 : 1237);
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!super.equals(obj))
			return false;
		if (getClass() != obj.getClass())
			return false;
		MessageFormat other = (MessageFormat) obj;
		if (appId != other.appId)
			return false;
		if (proxiable != other.proxiable)
			return false;
		if (request != other.request)
			return false;
		return true;
	}

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(getClass().getSimpleName()).append(" [");
        buf.append("code=").append(getCode()).append(", ");
        buf.append("abbrev=").append(getName()).append(", ");
        buf.append("request=").append(request).append(", ");
        buf.append("proxiable=").append(proxiable).append(", ");
        buf.append("appId=").append(appId).append(", ");
        if (fullName != null) {
            buf.append("name=").append(fullName).append(", ");
        }
        buf.append("rules=").append(rules);
        buf.append("]");
        return buf.toString();
    }
	
	/**
     * A MessageFormat.CmdF represents diameter header command flags
     * field[r-bit,p-bit,e-bit]
     * <p/>
     * <pre>
     *     &lt;DPR&gt;  ::= &lt; Diameter Header: 282, REQ &gt;
     *     &lt;DPA&gt;  ::= &lt; Diameter Header: 282 &gt;
     *
     *     &lt;STR&gt; ::= &lt; Diameter Header: 275, REQ, PXY &gt;
     *     &lt;STA&gt;  ::= &lt; Diameter Header: 275, PXY &gt;
     * </pre>
     */
     static enum CmdF {
        /**
         * r-bit=", REQ" ; If present, the 'R' bit in the Command ; Flags is
         * set, indicating that the message ; is a request, as opposed to an
         * answer.
         */
        REQ,

        /**
         * p-bit=", PXY" ; If present, the 'P' bit in the Command ; Flags is
         * set, indicating that the message ; is proxiable.
         */
        PXY,

        /**
         * e-bit=", ERR" ; If present, the 'E' bit in the Command ; Flags is
         * set, indicating that the answer ; message contains a Result-Code AVP
         * in ; the "protocol error" class.
         */
        ERR
    }

    public static class Builder {
        // Required parameters
        private final int code;

        // Optional parameters - initialized to default values
        private String name; // CER
        private boolean request = false;
        private boolean proxiable = false;
        private int appId = 0;

        private String fullName; // Capabilities-Exchange-Request

        /**
         * @param code Command-Code(257-CER)
         */
        public Builder(int code) {
            this.code = code;
        }

        /**
         * @param name Command-Abbrev(CER)
         * @return This Builder
         */
        public Builder name(String name) {
            this.name = name;
            return this;
        }

        /**
         * @param fullName Command-Name(Capabilities-Exchange-Request)
         * @return This Builder
         */
        public Builder fullName(String fullName) {
            this.fullName = fullName;
            return this;
        }

        public Builder request(boolean request) {
            this.request = request;
            return this;
        }

        public Builder proxiable(boolean proxiable) {
            this.proxiable = proxiable;
            return this;
        }

        public Builder appId(int appId) {
            this.appId = appId;
            return this;
        }

        public MessageFormat build() {
            return new MessageFormat(code, name, request, proxiable, appId, fullName);
        }
    }
}
